package com.fary.base;

import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.baomidou.mybatisplus.core.toolkit.Assert;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Constants;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.github.yulichang.base.MPJBaseMapper;
import org.apache.ibatis.annotations.Param;

import java.io.Serializable;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

public interface BaseMapper<T> extends MPJBaseMapper<T> {

    /**
     * 根据 ID 查询
     * @param id 主键ID
     */
    default T getById(Serializable id) {
        return selectById(id);
    }

    /**
     * 根据某个字段查询实体
     * @param column 实体字段get方法
     * @param value 字段的值
     * @param <R> get方法返回类型
     */
    default <R> T getByColumn(SFunction<T, R> column, Object value) {
        LambdaQueryWrapper<T> lambdaQueryWrapper = new LambdaQueryWrapper<T>()
                .eq(column, value);
        return selectOne(lambdaQueryWrapper);
    }

    /**
     * 根据某个字段查询所有实体
     * @param column 实体字段get方法
     * @param value 字段的值
     * @param <R> get方法返回类型
     */
    default <R> List<T> listByColumn(SFunction<T, R> column, Object value) {
        LambdaQueryWrapper<T> lambdaQueryWrapper = new LambdaQueryWrapper<T>()
                .eq(column, value);
        return selectList(lambdaQueryWrapper);
    }

    /**
     * 根据条件查询某个字段的值
     * @param column 实体字段get方法
     * @param queryWrapper 实体对象封装操作类（可以为 null）
     * @param <R> 返回类型
     */
    default <R> R getColumnValue(SFunction<T, R> column, Wrapper<T> queryWrapper) {
        List<T> list = selectList(queryWrapper);
        if (CollectionUtils.isEmpty(list)) {
            return null;
        }
        return column.apply(list.get(0));
    }

    /**
     * 根据条件查询某个字段的所有值
     * @param column 实体字段get方法
     * @param queryWrapper 实体对象封装操作类（可以为 null）
     * @param <R> 返回类型
     */
    default <R> List<R> listColumnValues(SFunction<T, R> column, Wrapper<T> queryWrapper) {
        List<T> list = selectList(queryWrapper);
        if (CollectionUtils.isEmpty(list)) {
            return null;
        }
        return list.stream().map(column).collect(Collectors.toList());
    }


    /**
     * MyBatis-Plus 框架查询出多条会抛出异常，此处重写selectOne方法，多条数据只取第一条数据
     * 根据 entity 条件，查询一条记录
     * <p>查询一条记录，例如 qw.last("AND limit 1") 限制取一条记录, 注意：多条数据会报异常</p>
     *
     * @param queryWrapper 实体对象封装操作类（可以为 null）
     */
    default T selectOne(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper) {
        List<T> ts = this.selectList(queryWrapper);
        if (CollectionUtils.isNotEmpty(ts)) {
            return ts.get(0);
        }
        return null;
    }

    /**
     * TableId 注解存在更新记录，否插入一条记录
     * @param entity 实体对象
     */
    default void saveOrUpdate(T entity) {
        Assert.notNull(entity, "error: can not execute. because entity is null!");
        TableInfo tableInfo = TableInfoHelper.getTableInfo(entity.getClass());
        Assert.notNull(tableInfo, "error: can not execute. because can not find cache of TableInfo for entity!");
        String keyProperty = tableInfo.getKeyProperty();
        Assert.notEmpty(keyProperty, "error: can not execute. because can not find column for id from entity!");
        Object idVal = tableInfo.getPropertyValue(entity, tableInfo.getKeyProperty());
        if (idVal == null || "".equals(idVal) || Objects.isNull(selectById((Serializable) idVal))) {
            insert(entity);
        } else {
            updateById(entity);
        }
    }

    /**
     * 根据某个字段删除
     * @param column 实体字段get方法
     * @param value 字段的值
     * @param <R> get方法返回类型
     */
    default <R> int deleteByColumn(SFunction<T, R> column, Object value) {
        LambdaQueryWrapper<T> lambdaQueryWrapper = new LambdaQueryWrapper<T>()
                .eq(column, value);
        return delete(lambdaQueryWrapper);
    }

}
